package com.oreilly.javaxslt.util;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

import net.spatula.news.BrowserCategory;

/**
 * A utility class that uses the Servlet 2.3 Filtering API to apply
 * an XSLT stylesheet to a servlet response.
 *
 * @author Eric M. Burke
 */
public class StylesheetFilter implements Filter {
    private FilterConfig filterConfig;
    private String xsltFilePath;

    /**
     * This method is called once when the filter is first loaded.
     */
    public void setFilterConfig(FilterConfig newConf) throws ServletException {
	init(newConf);
    }

    public FilterConfig getFilterConfig() {
	return filterConfig;
    } 

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;

        // xsltPath should be something like "/WEB-INF/xslt/"
        String xsltPath = filterConfig.getInitParameter("xsltPath");
        if (xsltPath == null) {
            throw new UnavailableException(
                    "xsltPath is a required parameter. Please "
                    + "check the deployment descriptor.");
        }

        // convert the context-relative path to a physical path name
        this.xsltFilePath = filterConfig.getServletContext()
                .getRealPath(xsltPath) + "/";

        // verify that the file exists
        
        /*
        if (this.xsltFilePath == null ||
                !new File(this.xsltFilePath).exists()) {
            throw new UnavailableException(
                    "Unable to locate stylesheet Path : " + this.xsltFilePath, 30);
        }
        */
    }

    public void doFilter (ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        String xsltFileName;

        if (!(res instanceof HttpServletResponse)) {
            throw new ServletException("This filter only supports HTTP");
        }

        BufferedHttpResponseWrapper responseWrapper =
                new BufferedHttpResponseWrapper((HttpServletResponse) res);
        System.out.println("Doing filter...");
        chain.doFilter(req, responseWrapper);
        System.out.println("Filter done.");

        // Tomcat 4.0 reuses instances of its HttpServletResponse
        // implementation class in some scenarios. For instance, hitting
        // reload() repeatedly on a web browser will cause this to happen.
        // Unfortunately, when this occurs, output is never written to the
        // BufferedHttpResponseWrapper's OutputStream. This means that the
        // XML output array is empty when this happens. The following
        // code is a workaround:
        
        // added NRJ: sometimes flush isn't called and there's still stuff pending 
        // in the printwriter buffer.
        responseWrapper.getWriter().flush();
        
        
        byte[] origXML = responseWrapper.getBuffer();
        System.out.println("origXML:" + origXML.toString());
        if (origXML == null || origXML.length == 0) {
            // just let Tomcat deliver its cached data back to the client
            System.out.println("Original XML was empty or null; passing through.");
            chain.doFilter(req, res);
            return;
        }

        if (((HttpServletRequest)req).getMethod().equals("HEAD")) {
            res.getOutputStream().write(origXML);
            res.flushBuffer();
            return;
        }
        

        try {
            // Figure out what kind of browser we're dealing with.
            int browserCategory = BrowserCategory.getBrowserCategory((HttpServletRequest)req);
            // translate that into a pathname
            xsltFileName = xsltFilePath + "type-" + browserCategory + ".xslt";
            // do the XSLT transformation
            System.out.println("Filename for stylesheet is "+xsltFileName);
            Transformer trans = StylesheetCache.newTransformer(xsltFileName);

            System.out.println("IN: " + new String(origXML));
            ByteArrayInputStream origXMLIn = new ByteArrayInputStream(origXML);
            Source xmlSource = new StreamSource(origXMLIn);

            ByteArrayOutputStream resultBuf = new ByteArrayOutputStream();
            trans.transform(xmlSource, new StreamResult(resultBuf));

            System.out.println("OUT: " + resultBuf.toString());
            
            res.setContentLength(resultBuf.size());
            res.setContentType("text/html");
            res.getOutputStream().write(resultBuf.toByteArray());
            res.flushBuffer();
        } catch (TransformerException te) {
            te.printStackTrace();
            throw new ServletException(te);
        }
    }

    /**
     * The counterpart to the init() method.
     */
    public void destroy() {
        this.filterConfig = null;
    }
}
